﻿using System;
using System.Globalization;

namespace GeoAPI.Geometries
{
	[Serializable]
	public class Coordinate : ICoordinate, ICloneable, IComparable, IComparable<ICoordinate>, IEquatable<ICoordinate>, IComparable<Coordinate>, IEquatable<Coordinate>
	{
		public Coordinate(double x, double y, double z)
		{
			this.X = x;
			this.Y = y;
			this.Z = z;
		}

		public double this[Ordinate ordinateIndex]
		{
			get
			{
				switch (ordinateIndex)
				{
				case Ordinate.X:
					return this.X;
				case Ordinate.Y:
					return this.Y;
				case Ordinate.Z:
					return this.Z;
				default:
					throw new ArgumentOutOfRangeException("ordinateIndex");
				}
			}
			set
			{
				switch (ordinateIndex)
				{
				case Ordinate.X:
					this.X = value;
					return;
				case Ordinate.Y:
					this.Y = value;
					return;
				case Ordinate.Z:
					this.Z = value;
					return;
				default:
					throw new ArgumentOutOfRangeException("ordinateIndex");
				}
			}
		}

		public Coordinate() : this(0.0, 0.0, double.NaN)
		{
		}

		[Obsolete]
		public Coordinate(ICoordinate c) : this(c.X, c.Y, c.Z)
		{
		}

		public Coordinate(Coordinate c) : this(c.X, c.Y, c.Z)
		{
		}

		public Coordinate(double x, double y) : this(x, y, double.NaN)
		{
		}

		public Coordinate CoordinateValue
		{
			get
			{
				return this;
			}
			set
			{
				this.X = value.X;
				this.Y = value.Y;
				this.Z = value.Z;
			}
		}

		public bool Equals2D(Coordinate other)
		{
			return this.X == other.X && this.Y == other.Y;
		}

		public bool Equals2D(Coordinate c, double tolerance)
		{
			return Coordinate.EqualsWithTolerance(this.X, c.X, tolerance) && Coordinate.EqualsWithTolerance(this.Y, c.Y, tolerance);
		}

		private static bool EqualsWithTolerance(double x1, double x2, double tolerance)
		{
			return System.Math.Abs(x1 - x2) <= tolerance;
		}

		public override bool Equals(object other)
		{
			if (other == null)
			{
				return false;
			}
			Coordinate coordinate = other as Coordinate;
			if (coordinate != null)
			{
				return this.Equals(coordinate);
			}
			return other is ICoordinate && ((IEquatable<ICoordinate>)this).Equals((ICoordinate)other);
		}

		public bool Equals(Coordinate other)
		{
			return this.Equals2D(other);
		}

		public int CompareTo(object o)
		{
			Coordinate other = (Coordinate)o;
			return this.CompareTo(other);
		}

		public int CompareTo(Coordinate other)
		{
			if (this.X < other.X)
			{
				return -1;
			}
			if (this.X > other.X)
			{
				return 1;
			}
			if (this.Y < other.Y)
			{
				return -1;
			}
			if (this.Y <= other.Y)
			{
				return 0;
			}
			return 1;
		}

		public bool Equals3D(Coordinate other)
		{
			return this.X == other.X && this.Y == other.Y && (this.Z == other.Z || (double.IsNaN(this.Z) && double.IsNaN(other.Z)));
		}

		public bool EqualInZ(Coordinate c, double tolerance)
		{
			return Coordinate.EqualsWithTolerance(this.Z, c.Z, tolerance);
		}

		public override string ToString()
		{
			return string.Concat(new string[]
			{
				"(",
				this.X.ToString("R", NumberFormatInfo.InvariantInfo),
				", ",
				this.Y.ToString("R", NumberFormatInfo.InvariantInfo),
				", ",
				this.Z.ToString("R", NumberFormatInfo.InvariantInfo),
				")"
			});
		}

		public object Clone()
		{
			return new Coordinate(this.X, this.Y, this.Z);
		}

		public double Distance(Coordinate c)
		{
			double num = this.X - c.X;
			double num2 = this.Y - c.Y;
			return System.Math.Sqrt(num * num + num2 * num2);
		}

		public double Distance3D(Coordinate c)
		{
			double num = this.X - c.X;
			double num2 = this.Y - c.Y;
			double num3 = this.Z - c.Z;
			return System.Math.Sqrt(num * num + num2 * num2 + num3 * num3);
		}

		public override int GetHashCode()
		{
			int num = 17;
			num = 37 * num + Coordinate.GetHashCode(this.X);
			return 37 * num + Coordinate.GetHashCode(this.Y);
		}

		public static int GetHashCode(double value)
		{
			long num = BitConverter.DoubleToInt64Bits(value);
			return (int)(num ^ num >> 32);
		}

		[Obsolete]
		double ICoordinate.X
		{
			get
			{
				return this.X;
			}
			set
			{
				this.X = value;
			}
		}

		[Obsolete]
		double ICoordinate.Y
		{
			get
			{
				return this.Y;
			}
			set
			{
				this.Y = value;
			}
		}

		[Obsolete]
		double ICoordinate.Z
		{
			get
			{
				return this.Z;
			}
			set
			{
				this.Z = value;
			}
		}

		[Obsolete]
		double ICoordinate.M
		{
			get
			{
				return double.NaN;
			}
			set
			{
			}
		}

		[Obsolete]
		ICoordinate ICoordinate.CoordinateValue
		{
			get
			{
				return this;
			}
			set
			{
				this.X = value.X;
				this.Y = value.Y;
				this.Z = value.Z;
			}
		}

		[Obsolete]
		double ICoordinate.this[Ordinate index]
		{
			get
			{
				switch (index)
				{
				case Ordinate.X:
					return this.X;
				case Ordinate.Y:
					return this.Y;
				case Ordinate.Z:
					return this.Z;
				default:
					return double.NaN;
				}
			}
			set
			{
				switch (index)
				{
				case Ordinate.X:
					this.X = value;
					return;
				case Ordinate.Y:
					this.Y = value;
					return;
				case Ordinate.Z:
					this.Z = value;
					return;
				default:
					return;
				}
			}
		}

		[Obsolete]
		bool ICoordinate.Equals2D(ICoordinate other)
		{
			return this.X == other.X && this.Y == other.Y;
		}

		[Obsolete]
		bool IEquatable<ICoordinate>.Equals(ICoordinate other)
		{
			return ((ICoordinate)this).Equals2D(other);
		}

		[Obsolete]
		int IComparable<ICoordinate>.CompareTo(ICoordinate other)
		{
			if (this.X < other.X)
			{
				return -1;
			}
			if (this.X > other.X)
			{
				return 1;
			}
			if (this.Y < other.Y)
			{
				return -1;
			}
			if (this.Y <= other.Y)
			{
				return 0;
			}
			return 1;
		}

		int IComparable.CompareTo(object o)
		{
			Coordinate other = (Coordinate)o;
			return this.CompareTo(other);
		}

		[Obsolete]
		bool ICoordinate.Equals3D(ICoordinate other)
		{
			return this.X == other.X && this.Y == other.Y && (this.Z == other.Z || (double.IsNaN(this.Z) && double.IsNaN(other.Z)));
		}

		[Obsolete]
		double ICoordinate.Distance(ICoordinate p)
		{
			double num = this.X - p.X;
			double num2 = this.Y - p.Y;
			return System.Math.Sqrt(num * num + num2 * num2);
		}

		public const double NullOrdinate = double.NaN;

		public double X;

		public double Y;

		public double Z;
	}
}
